'use strict'

const http = require('http')
const fs = require('fs')
const {resolve} = require('path');
const { createRequireFromPath } = require('module');
// const { response } = require('express');
const fsp = fs.promises;

 /**--------------------------------------------解析Body数据------------------------------------------- */ 
/**
 * Content-Disposition:from-data;name:"file";filename:"xxx"
 * Content-Type:text/plain
 * 
 * @param {string} headerData 
 */

 function parseBodyHeaders(headerData){
    let hlist = headerData.split("\r\n").filter(p =>p.length>0)

    //解析content-disposition => hlist[0]
    // 按照";"切分数组，选出从第二个元素开始的数组
    let csplist = hlist[0].split(';').splice(1)
    let filename 
    let name
    for(let a of csplist){
        // if(a.indexOf('filename=')===0){
        if(a.indexOf(filename)===0){
            filename = a.substring(a.indexOf('=')+2,a.length-1)
        }else{
            name = a.substring(a.indexOf('=')+2,a.length-1)
        }
    }

    let headers={}
    let ind = 0
    let k
    for(let h of hlist){
        ind = h.indexOf(':')
        k = h.substring(0,ind).toLowerCase()
        headers[k] = h.substring(ind+1).trim()
    }
    return {
        filename,
        name,
        headers,
        contentType:headers['content-type'] || ''
    }
}

function parseBody(bodyData,headers){
    let ctype = headers['content-type'] 
    let bdy = ctype.substring(ctype.indexOf('=') + 1)
    let crlf_bdy = `\r\n--${bdy}`
    let header_end_index = bodyData.indexOf('\r\n\r\n',crlf_bdy.length)
    let headerData = bodyData.toString('utf8',crlf_bdy.length,header_end_index)

    //解析文件头信息
    let hd = parseBodyHeaders(headerData)

    let file = {
        start:header_end_index + 4,
        end:bodyData.indexOf(crlf_bdy,header_end_index),
        ...hd
    }

    file.length = file.end - file.start
    return file
}

//解析文件扩展名
function parseExtname(filename){
    if(filename.length<2){
        return ''
    }

    let nsplit = filename.split('.').filter(p=>p.length>0)
    if(nsplit.length<2) return ''
    return `.${nsplit[nsplit.length-1]}`
}

 /**------------------------------------------解析Body数据 - END--------------------------------------- */ 


let html=`
<!DOCTYPE html>
<html>
    <head><title>first HTTP</title></head>
    <body>
        你好。
    </body>
</html>
`

let routerTable = {
    GET:{
        '/':async(ctx)=>{
            ctx.response.setHeader('content-type','text/html;charset=utf-8');//构造消息头
                // // text/plain:不解析HTML代码
                // // text/HTML：解析HTML代码
            ctx.end(html)
        },
    
        '/test':async(ctx)=>{
            let status = parseInt(Math.random() * 200) + 199
            ctx.response.statusCode = status
            ctx.end('test end')
        },

        '/upload':async(ctx)=>{
            let stm = fs.createReadStream('./upload.html')
            stm.pipe(ctx.response,{end:false}) //不会把response进行自动执行操作；注意默认值
            stm.on('end',()=>{
                ctx.end();
            })
        }
    },

    POST:{
        '/data':async(ctx)=>{             //接收数据

            let file = parseBody(ctx.rawBody,ctx.headers)
            console.log(file)

            if(!ctx.rawBody){
                ctx.end();
                return;
            }
            let fname = `${Date.now()}--${Math.random()}` + `${parseExtname(file,filename)}`
            let fh = await fsp.open(fname,'w+')
            let fret = await fh.write(ctx.rawBody,file.start,file.length,0)
            fh.close();  //关闭打开的文件
            ctx.end(`${fret.bytesWritten}`)///////////
            
                
        //     else
        //         ctx.end();
        }  
    }
}



http.createServer(async(request,response)=>{
    let rm = routerTable[request.method]          //[req.method] ：获取请求方法

    let usplit = request.url.split('?')
    let pathname = usplit[0]
    let querystring = usplit[1] || ''

    if(!rm || !rm[pathname]){                 //res.url :/upload?a=123
        response.statusCode = 404
        response.end('page not found')
        return
    }

    //cte是context的缩写，意为请求上下文，就是打包了各种数据和方法的对象
    let ctx = {
        method:request.method,
        path:pathname,
        search:querystring,
        headers:request.headers,
        response:response,
        request:request,
        end:(data,encoding = 'utf8')=>{
            response.end(data,encoding)
        },
        write:(data,encoding = 'utf8')=>{
            response.write(data,encoding)
        },
        totalLength:0,
        rawBody:null,
        bufferList:[]
    }

    await new Promise((rv,rj)=>{
        if('PD'.indexOf(request.method[0])>=0){
            request.on('data',chunk=>{
                 //处理body数据
                ctx.bufferList.push(chunk)
                ctx.totalLength += chunk.length
            })
        }else{
            request.on('data',data=>{
                response.statusCode=400
                response,end('bad resquest',()=>{
                    request.destroy()
                })
            })
        }

        let resolved = false;
        request.on('end',()=>{     //当接收数据没有时触发
            resolved = true;
            rv();
        })
        request.on('close',()=>{     //关闭时触发
            (!resolved) && rv();
        })
    })

    if(!response.writable || response.writableEnded){
        return
    }

    if(ctx.bufferList.length>0){
        ctx.rawBody = Buffer.concat(ctx.bufferList,ctx.totalLength)
        ctx.bufferList = []
    }
    rm[pathname](ctx)
}).listen(3456)  //端口 受保护的消息队列